package com.example.message

import com.jakewharton.rxrelay2.PublishRelay
import com.jakewharton.rxrelay2.Relay
import io.reactivex.Observable
import io.reactivex.ObservableSource
import java.util.TreeMap
import java.util.concurrent.ConcurrentHashMap

/**
 * 单例
 */
object MessageBus : IMessageBus {


    private val relayMap: TreeMap<Int, Relay<Any>> = TreeMap()
    private val stickyEvents: MutableMap<Class<*>, Any> = ConcurrentHashMap()


    override fun post(messageEvent: MessageEvent) {
        synchronized(relayMap){
            for (relay in relayMap.descendingMap().values){
                relay.accept(messageEvent)
            }
        }
    }

    override fun postSticky(messageEvent: MessageEvent) {
        stickyEvents[messageEvent.javaClass] = messageEvent
        post(messageEvent)
    }

    override fun <T : MessageEvent> toObservable(eventType: Class<T>): Observable<T> {
        return toObservable(eventType, false)
    }

    override fun <T : MessageEvent> toObservableSticky(eventType: Class<T>): Observable<T> {
        return toObservable(eventType, true)
    }


    private fun <T : MessageEvent> toObservable(
        eventType: Class<T>,
        isStick: Boolean
    ): Observable<T> {
        val relay: Relay<Any>
        synchronized(relayMap) {
            if (relayMap[0] == null) {
                relay = PublishRelay.create<Any?>().toSerialized()
                relayMap[0] = relay
            } else {
                relay = relayMap[0]!!
            }
        }

        val observable = relay.ofType(eventType)
        if (!isStick) {
            return observable
        }

        val event = stickyEvents[eventType]
        return if (event != null) {
            observable.mergeWith(io.reactivex.Observable.create { observableEmitter ->
                eventType.cast(event)?.let {
                    observableEmitter.onNext(it)
                }
            })
        } else {
            observable
        }
    }


    override fun clearSticky() {
        stickyEvents.clear()
    }

    override fun clearSticky(clazz: Class<*>) {
        stickyEvents.remove(clazz)
    }
}